<?php

/**
 * This is the model class for table "msgg_summary".
 *
 * The followings are the available columns in table 'msgg_summary':
 * @property integer $id
 * @property string $target
 * @property string $type
 * @property string $sub
 * @property string $direction
 * @property integer $ts
 * @property integer $duration
 * @property integer $status
 * @property integer $sum
 * @property integer $time_update
 **/
class MsggSummary extends CActiveRecord
{


    const DURATION_MINUTE = 60;
    const DURATION_HOUR = 3600;
    const DURATION_DAY = 86400;

    const TARGET_ITEM = 'item';
    const TARGET_LOG = 'log';

    const DIRECTION_INPUT='input';
    const DIRECTION_OUTPUT='output';

    const TIMEZONE = 8;

    private static $_options = [];

    /**
     * @return string the associated database table name
     */
    public function tableName()
    {
        return 'msgg_summary';
    }

    /**
     * @return array validation rules for model attributes.
     */
    public function rules()
    {
        return array(
            array('ts, duration, sum,time_update,status', 'numerical', 'integerOnly' => true),
            array('target,type, sub,direction', 'length', 'max' => 32),
            array('id,target, type, sub,direction, ts, duration, sum, status', 'safe', 'on' => 'search'),
            array('time_update', 'default', 'value' => time(), 'setOnEmpty' => false, 'on' => 'update'),
            array('time_update', 'default', 'value' => time(), 'setOnEmpty' => false, 'on' => 'insert'),

        );
    }

    /**
     * @return array relational rules.
     */
    public function relations()
    {
        return array();
    }

    /**
     * @return array customized attribute labels (name=>label)
     */
    public function attributeLabels()
    {
        return array(
            'id' => 'ID',
            'target' => 'Target',
            'type' => 'Type',
            'sub' => 'Sub',
            'direction' => 'Direction',
            'ts' => 'Ts',
            'duration' => 'Duration',
            'sum' => 'Sum',
            'status' => 'Status',
            'time_update' => 'Time Update'
        );
    }

    /**
     * Retrieves a list of models based on the current search/filter conditions.
     *
     * Typical usecase:
     * - Initialize the model fields with values from filter form.
     * - Execute this method to get CActiveDataProvider instance which will filter
     * models according to data in model fields.
     * - Pass data provider to CGridView, CListView or any similar widget.
     *
     * @return CActiveDataProvider the data provider that can return the models
     * based on the search/filter conditions.
     */
    public function search()
    {

        $criteria = new CDbCriteria;
        $criteria->compare('id', $this->id);
        $criteria->compare('target', $this->target, true);
        $criteria->compare('type', $this->type, true);
        $criteria->compare('sub', $this->sub, true);
        $criteria->compare('direction', $this->direction, true);
        $criteria->compare('ts', $this->ts);
        $criteria->compare('duration', $this->duration);
        $criteria->compare('sum', $this->sum);
        $criteria->compare('status', $this->status);
        return new CActiveDataProvider($this, array(
            'criteria' => $criteria,
        ));
    }

    /**
     * Returns the static model of the specified AR class.
     * Please note that you should have this exact method in all your CActiveRecord descendants!
     * @param string $className active record class name.
     * @return MsggSummary the static model class
     */
    public static function model($className = __CLASS__)
    {
        return parent::model($className);
    }

    public static function getOptions($attribute, $options = array())
    {
        if (!isset(self::$_options[$attribute])) {
            $opts = [];
            switch ($attribute) {
                case 'view_type':
                    $opts['summary_input'] = '网关入口总览';
                    $opts['summary_output'] = '网关出口总览';
                    $opts['summary_input_mail_detail'] = '邮件入口详情';
                    $opts['summary_output_mail_detail'] = '邮件出口详情';
                    $opts['summary_input_wechat_detail'] = '微信入口详情';
                    $opts['summary_output_wechat_detail'] = '微信出口详情';
                    $opts['summary_input_call_detail'] = '电话入口详情';
                    $opts['summary_output_call_detail'] = '电话出口详情';
                    $opts['summary_input_sms_detail'] = '短信入口详情';
                    $opts['summary_output_sms_detail'] = '短信出口详情';
                    break;
            }
            self::$_options[$attribute] = $opts;
        }
        if (is_array($options) && !empty($options)) {
            foreach (self::$_options[$attribute] as $k => $v) {
                $options[$k] = $v;
            }
        } else {
            $options = self::$_options[$attribute];
        }
        return $options;
    }

    public static function builds($params = [])
    {
        $result = array('code' => 0, 'msg' => '');
        $ts = !isset($params['ts']) ? time() : $params['ts'];
        $duration = !isset($params['duration']) ? self::DURATION_MINUTE : $params['duration'];
        switch ($duration) {
            case self::DURATION_MINUTE:
                $ts = self::getMinuteBase($ts);
                break;
            case self::DURATION_HOUR:
                $ts = self::getHourBase($ts);
                break;
            case self::DURATION_DAY:
                $ts = self::getDayBase($ts);
                break;
            default:
                return;
        }
        try {
            for ($i = 1; $i <= self::getWindowSize($duration); $i++) {
                $start = $ts - $i * $duration;
                self::analyze($start, $duration);
            }

        } catch (Exception $e) {
            $result['code'] = $e->getCode();
            $result['msg'] = $e->getMessage();
        }
        return $result;
    }

    //for api
    public static function statistics($ts, $type, $duration)
    {
        $result = array('code' => 0, 'msg' => '');
        try {
            switch ($duration) {
                case self::DURATION_MINUTE:
                    $ts = self::getMinuteBase($ts);
                    break;
                case self::DURATION_HOUR:
                    $ts = self::getHourBase($ts);
                    break;
                case self::DURATION_DAY:
                    $ts = self::getDayBase($ts);
                    break;
                default:
                    return;
            }
            $end = $ts + $duration -1;
            $ts_date = date("Y-m-d H:i:s", $ts);
            $end_date = date("Y-m-d H:i:s", $end);

            $inputNum = MsggItem::model()->count(' t.`time_create` >= :ts and t.`time_create` <=:end and t.`type`=:type',
                array(':ts' => $ts_date, ':end' => $end_date, ':type' => $type));
            $outNum = MsggChannelLog::model()->count('t.`time` >= :ts and t.`time`<=:end and type=:type',
                array(':ts' => $ts, ':end' => $end, ':type' => $type));
            $data = array('input_total' => $inputNum, 'out_total' => $outNum,);
            $result['data'] = $data;

        } catch (Exception $e) {
            $result['code'] = $e->getCode();
            $result['msg'] = $e->getMessage();
        }
        return $result;
    }

    private static function analyze($start, $duration = self::DURATION_MINUTE)
    {
        switch ($duration) {
            case self::DURATION_HOUR:
                $ts = self::getHourBase($start);
                break;
            case self::DURATION_DAY:
                $ts = self::getDayBase($start);
                break;
            default :
            case self::DURATION_MINUTE:
                $ts = self::getMinuteBase($start);
                break;

        }
        $iStart = $ts;
        $iEnd = $iStart + $duration - 1;
        $statusMap = MsggItem::getOptions('status');
        $subs = [];
        $reader =new ChannelConfigReader();
        $closeReader = new ChannelConfigReader(null, MsggConfig::STATUS_CLOSE);
        $configs = array_merge(MsggConfig::readConfigs($reader),MsggConfig::readConfigs($closeReader));
        foreach ($configs as $config) {
            $type = $config->getType();
            $name = $config->getName();
            $sub = implode('_',[$type, $name]);
            $subs[$type][] = $sub;
        }
        //item input
        $itemInput = array();
        $itemOut =array();
        $logs = array();
        $inputItems = MsggItem::model()->findAll('t.time >= :start and t.time <=:end',
            [":start" => $iStart, ':end' => $iEnd]);
        foreach ($subs as $type => $sub) {
            $itemInput[$type]=0 ;
            foreach ($sub as $s) {
                foreach ($statusMap as $status =>$value)
                {
                    $itemOut[$type][$s][$status]=0;
                    $logs[$type][$s][$status] = 0;
                }
            }
        }
        foreach ($inputItems as $item)
        {
            if (!isset($itemInput[$item->type])) {
                $itemInput[$item->type]=0;
            }
            $itemInput[$item->type]+=1;
        }


        $outItems = MsggItem::model()->findAll('t.time_renewal >= :start and t.time_renewal <=:end',
            [":start" => $iStart, ':end' => $iEnd]);
        //count
        foreach ($outItems as $item) {
            //item
            $type = $item->type;
            $sub = $item->channel;
            $status = $item->status;
            if (!isset($itemOut[$type])) {
                $itemOut[$type] = [];
            }
            if (!isset($itemOut[$type][$sub])) {
                $itemOut[$type][$sub] = [];
            }
            if (!isset($itemOut[$type][$sub][$status])) {
                $itemOut[$type][$sub][$status] = 0;
            }
            $itemOut[$type][$sub][$status]++;
        }

        $channelLogs = MsggChannelLog::model()->findAll('t.time >= :start and t.time <=:end',
            [":start" => $iStart, ':end' => $iEnd]);
        foreach ($channelLogs as $log) {
            //item
            $type = $log->type;
            $sub = $log->channel;
            $status = $log->status;
            if (!isset($logs[$type])) {
                $logs[$type] = [];
            }
            if (!isset($logs[$type][$sub])) {
                $logs[$type][$sub] = [];
            }
            if (!isset($logs[$type][$sub][$status])) {
                $logs[$type][$sub][$status] = 0;
            }
            $logs[$type][$sub][$status]++;
        }
        //update or save
        foreach ($itemInput as $type => $val) {
            $attributes = array(
                'target' => self::TARGET_ITEM,
                'type' => $type,
                'direction' => self::DIRECTION_INPUT,
                'ts' => $ts,
                'duration' => $duration,
                'sum' => empty($val) ? 0 : $val,
            );

            $affectRows = self::model()->updateAll(
                ['sum' => empty($val) ? 0 : $val, 'time_update' => time()],
                'target=:target and ' .
                'type=:type and ' .
                'direction=:direction and ' .
                'ts=:ts and ' .
                'duration=:duration ',
                array(
                    ':target' => self::TARGET_ITEM,
                    ':type' => $type,
                    ':direction' => self::DIRECTION_INPUT,
                    ':ts' => $ts,
                    ':duration' => $duration,
                )
            );

            if (!$affectRows) {
                $record = new self();
                $record->attributes = $attributes;
                $record->save();
            }
        }
        foreach ($itemOut as $type => $subs) {
            foreach ($subs as $sub => $statuses) {
                foreach ($statuses as $status => $val) {
                    $attributes = array(
                        'target' => self::TARGET_ITEM,
                        'type' => $type,
                        'sub' => $sub,
                        'direction' => self::DIRECTION_OUTPUT,
                        'ts' => $ts,
                        'duration' => $duration,
                        'sum' => empty($val) ? 0 : $val,
                        'status' => $status,
                    );
                    $affectRows = self::model()->updateAll(
                        ['sum' => empty($val) ? 0 : $val, 'time_update' => time()],
                        'target=:target and ' .
                        'sub=:sub and ' .
                        'type=:type and ' .
                        'direction=:direction and ' .
                        'ts=:ts and ' .
                        'duration=:duration and ' .
                        'status=:status',
                        array(
                            ':target' => self::TARGET_ITEM,
                            ':sub' => $sub,
                            ':type' => $type,
                            ':direction' => self::DIRECTION_OUTPUT,
                            ':ts' => $ts,
                            ':duration' => $duration,
                            ':status' => $status,
                        )
                    );

                    if (!$affectRows) {
                        $record = new self();
                        $record->attributes = $attributes;
                        $record->save();
                    }
                }

            }
        }
        foreach ($logs as $type => $subs) {
            foreach ($subs as $sub => $statuses) {
                foreach ($statuses as $status => $val) {
                    $attributes = array(
                        'target' => self::TARGET_LOG,
                        'type' => $type,
                        'sub' => $sub,
                        'ts' => $ts,
                        'duration' => $duration,
                        'sum' => empty($val) ? 0 : $val,
                        'status' => $status,
                    );
                    $affectRows = self::model()->updateAll(
                        ['sum' => empty($val) ? 0 : $val, 'time_update' => time()],
                        'target=:target and ' .
                        'sub=:sub and ' .
                        'type=:type and ' .
                        'ts=:ts and ' .
                        'duration=:duration and ' .
                        'status=:status',
                        array(
                            ':target' => self::TARGET_LOG,
                            ':sub' => $sub,
                            ':type' => $type,
                            ':ts' => $ts,
                            ':duration' => $duration,
                            'status' => $status,
                        )
                    );

                    if (!$affectRows) {
                        $record = new self();
                        $record->attributes = $attributes;
                        $record->save();
                    }
                }

            }
        }

    }

    private static function getWindowSize($duration = self::DURATION_MINUTE)
    {
        switch ($duration) {
            case self::DURATION_MINUTE:
                return 5;
                break;
            case self::DURATION_HOUR:
                return 1;
                break;
            case self::DURATION_DAY:
                return 1;
                break;
            default:
                return 3;
                break;
        }
    }

    public static function getData($ts = null, $duration = self::DURATION_MINUTE)
    {
        if (empty($ts)) {
            $ts = time();
        }
        $ts = self::getMinuteBase($ts);
        $summaries = MsggSummary::model()->findAll('`ts`>=:start and `duration`=:duration',
            [':start' => $ts, ':duration' => $duration]);
        if (empty($summaries)) {
            self::analyze($ts, $duration);
            $summaries = MsggSummary::model()->findAll('`ts`>=:start and `duration`=:duration',
                [':start' => $ts, ':duration' => $duration]);
        }
        $result = array();
        //gen
        foreach ($summaries as $summary) {
            $target = $summary->target;
            $sub = $summary->sub;
            $type = $summary->type;
            if (!array_key_exists($target, $result)) {
                $result[$target] = array('all' => 0);
            }
            if (!array_key_exists($type, $result[$target])) {
                $result[$target][$type] = array('all' => 0);
            }
            if (!array_key_exists($sub, $result[$target][$type])) {
                $result[$target][$type][$sub] = array('all' => 0);
            }
            if (!array_key_exists($summary->status, $result[$target][$type][$sub])) {
                $result[$target][$type][$sub][$summary->status] = 0;
            }
            $result[$target]['all'] += $summary->sum;
            $result[$target][$type]['all'] += $summary->sum;
            $result[$type][$sub]['all'] += $summary->sum;
            $result[$type][$sub][$summary->status] += $summary->sum;
        }
        return $result;
    }

    public static function getDayBase($ts = null)
    {
        if ($ts == null) {
            $ts = time();
        }
        return floor(($ts + 3600 * self::TIMEZONE) / self::DURATION_DAY) * self::DURATION_DAY - 3600 * self::TIMEZONE;
    }

    public static function getHourBase($ts = null)
    {
        if ($ts == null) {
            $ts = time();
        }
        return floor(($ts + 3600 * self::TIMEZONE) / self::DURATION_HOUR) * self::DURATION_HOUR - 3600 * self::TIMEZONE;
    }

    public static function getMinuteBase($ts = null)
    {
        if ($ts == null) {
            $ts = time();
        }
        return floor(($ts + 3600 * self::TIMEZONE) / self::DURATION_MINUTE) * self::DURATION_MINUTE - 3600 * self::TIMEZONE;
    }


}



